/*
 *  Copyright 2010 Massimiliano Mazzarolo
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0
 *        
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License. 
 */
package grails.coherence

import grails.coherence.extractor.GroovyPropertyValueExtractor;

import com.tangosol.net.NamedCache
import com.tangosol.util.Filter
import com.tangosol.util.ValueExtractor;
import com.tangosol.util.filter.AllFilter
import com.tangosol.util.filter.AnyFilter
import com.tangosol.util.filter.EqualsFilter
import com.tangosol.util.filter.IsNullFilter
import com.tangosol.util.filter.NotFilter
import com.tangosol.util.extractor.AbstractExtractor;
import com.tangosol.util.extractor.KeyExtractor
import com.tangosol.util.extractor.PofExtractor;

import com.tangosol.util.extractor.ReflectionExtractor;
import com.tangosol.util.filter.AlwaysFilter;
import com.tangosol.util.filter.BetweenFilter
import com.tangosol.util.filter.ContainsAllFilter
import com.tangosol.util.filter.ContainsAnyFilter
import com.tangosol.util.filter.ContainsFilter
import com.tangosol.util.filter.GreaterEqualsFilter
import com.tangosol.util.filter.GreaterFilter
import com.tangosol.util.filter.InFilter
import com.tangosol.util.filter.InKeySetFilter
import com.tangosol.util.filter.IsNotNullFilter
import com.tangosol.util.filter.LessEqualsFilter
import com.tangosol.util.filter.LessFilter
import com.tangosol.util.filter.LimitFilter;

/**
 * Builder for Coherence {@link Filter filters}. Provides a sugar for most of the filters shipped
 * with Coherence itself plus some commodities for working with POF or key-extracted values. 
 * <br>
 * Simple example following...
 * <br>
 * <code>
 * <pre>
 * and {                                					// AllFilter
 *    not {                             					// NotFilter (applied to the inner filter chains)
 *      equals("getMethod", aValue)     					// EqualsFilter(String, object)
 *    }
 *    or {                              					// AnyFilter 
 *    equals(method("withParams", [1]), value)            	// EqualsFilter with parametrized method call.
 *    equals(new MyExtractor(), value) 						// EqualsFilter with custom ValueExtractor
 *    equals(key("getKeyValue"), value)						// EqualsFilter with KeyExtractor			
 *    equals(key("getKeyValue"), value)						// EqualsFilter with KeyExtractor			
 *    } 
 *    inKeySet([value1, value2]) {
 *      equals('key', 'value')
 *    }
 *    ...
 *  }
 *  </pre>
 *  </code>
 *
 * @author Massimiliano Mazzarolo
 * @since  0.1
 */
public class FilterBuilder {
	
	private def stackMap = [:]
	
	private def previousStack = null
	
	private MultiFilterContainer currentStack = new MultiFilterContainer(type: MultiFilterContainer.SINGLE)
	
	private Filter current = null
	
	void and(Closure inner) {    
		withMultiContainer(MultiFilterContainer.AND, inner)  { stack ->
			new AllFilter(stack as Filter[])
		}
	}
	
	void not(Closure inner) {
		withMultiContainer(MultiFilterContainer.SINGLE, inner) { stack ->
			new NotFilter(stack.process() as Filter)      
		}
	}
	
	void or(Closure inner) {
		withMultiContainer(MultiFilterContainer.OR, inner) { stack ->
			new AnyFilter(stack as Filter[])      
		}
	}
	
	void equals(def name, def value) {
		def eq = new EqualsFilter(name, value)
		currentStack.add eq
	}
	
	void isNull(String name) {
		def isn = new IsNullFilter(name)
		currentStack.add isn
	}
	
	void notNull(String name) {
		def isn = new IsNotNullFilter(name)
		currentStack.add isn
	}
		
	void between(def name, Map map) {
		currentStack.add new BetweenFilter(name, map.from, map.to)
	}
	
	void containsAll(def name, Set values) {
		currentStack.add new ContainsAllFilter(name, values)    
	}
	
	void containsAll(def name, List values) {
		containsAll(name, new LinkedHashSet(values))    
	}
	
	void containsAny(def name, Set values) {
		currentStack.add new ContainsAnyFilter(name, values)    
	}
	
	void containsAny(def name, List values) {
		containsAny name, new LinkedHashSet(values)     
	}
	
	void contains(def name, Object value) {
		currentStack.add(new ContainsFilter(name, value))     
	}
	
	void ge(def name, def value) {
		currentStack.add new GreaterEqualsFilter(name, value)
	}
	
	void gt(def name, def value) {
		currentStack.add new GreaterFilter(name, value)
	}
	
	void 'in'(def name, Set values)  {
		currentStack.add new InFilter(name, values)  
	}
	
	void 'in'(def name, List values)  {
		'in'(name, new LinkedHashSet(values))  
	}
	
	void inKeySet(Set values, Closure inner) {
		withMultiContainer(MultiFilterContainer.SINGLE, inner) { stack ->
			new InKeySetFilter(stack.process() as Filter, values)      
		}
	}
	
	void inKeySet(List values, Closure inner) {
		inKeySet(new LinkedHashSet(values), inner) 
	}
	
	void le(def name, def value) {
		currentStack.add new LessEqualsFilter(name, value)
	}
	
	void lt(def name, def value) {
		currentStack.add new LessFilter(name, value)
	}
	
	void limit(int currentpage, int pagesize, Closure inner) {
		withMultiContainer(MultiFilterContainer.SINGLE, inner) { stack ->
			def filter = new LimitFilter(stack.process() as Filter, pagesize)
			filter.page = currentpage
			return filter
		}		
	}
	
	void all() {
		currentStack.add AlwaysFilter.INSTANCE
	}
	
	////////////////////////// Value Extractors	
	
	ValueExtractor key(def name, def args = null) {
		switch (name?.class) {
			case ValueExtractor:
			new KeyExtractor(name)
			break
			case String:
			method(name, args, 'KEY')
			break
			default:
			throw new UnsupportedOperationException("Unsupported key() argument: ${name?.class}")
		}
	}
	
	ValueExtractor method(String name, Object[] args = null, def type = 'VALUE') {		
		new ReflectionExtractor(name, args, (type == 'KEY' ? AbstractExtractor.KEY : AbstractExtractor.VALUE))
	}
	
	ValueExtractor pof(Class clz, int pos) {
		new PofExtractor(clz, pos)
	}
	
	ValueExtractor p(String name){
		new GroovyPropertyValueExtractor(name);
	}
	
	
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	private void withMultiContainer(String type, Closure inner, Closure filterFactory) {
		
		inner.resolveStrategy = Closure.DELEGATE_FIRST
		inner.delegate = this    
		
		stackMap[currentStack] = previousStack
		previousStack = currentStack
		currentStack = new MultiFilterContainer(type: type)
		
		inner.call()
		
		def filter = filterFactory(currentStack)
		
		currentStack = previousStack
		previousStack = stackMap[currentStack]
		stackMap.remove(currentStack)
		
		currentStack << filter    
	}
	
	Filter getFilter() {
		currentStack.process()
	}
}